//
//  fileHandling.c
//  imageToASCII
//
//  Created by Benno Lauther on 09.01.14.
//  Copyright (c) 2014 Benno Lauther. All rights reserved.
//

#include <stdio.h>
#include "prototypes.h"
#include "typedefs.h"
#include <stdlib.h>

uint64_t readLittleEndian(FILE* inputStream, int numberOfBytes);


typedef struct
{
    uint32_t width;
    uint32_t height;
    uint16_t bpp;
} bmp_dib_header;


/**
* getImage
* creates an Image object based on the given file path
*
*
* @param 	imagePath       path to the image file you want to load into memory
* @return   Image* 			reference of the created image object, NULL if error
*/

Image*      getImage(char* imagePath)
{
    Image* image ;
    enum filetype imgFileType = getFileType(imagePath);
    
    switch (imgFileType)
    {
        case bmp:
            image = getBmp(imagePath);


            break;
        case invalid:
            printf("Invalid file!!!111elf\n");
            return NULL;
            break;
        default:
            break;
    }

    return image;
}

/**
* getFileType
* checks the file header for the filetype
*
* @param 	imagePath  	    path of the image file you want to check
* @return   enum filetype   type of the file
*/


enum filetype getFileType(char* imagePath)
{

    /* Read Magic Number for Filetype identification */
    FILE* inputStream = fopen(imagePath, "r");
    if(!inputStream)
    {
        return invalid;
    }
    uint16_t signature=0;

    signature |= fgetc(inputStream)<<8;
    signature |= fgetc(inputStream);
    fclose(inputStream);


    /* Match Magic Numbers */

    switch(signature)
    {
        case 0x424D :
                return bmp;
        break;

        default:
                return invalid;
        break;
    }


}

/**
* getBmp
* special function to load a Windows Bitmap into memory
*
*
* @param 	imagePath       Path of the bitmap you want to load
* @return   Image* 			reference of the image object created, NULL if error
*/

Image* getBmp(char* imagePath)
{
    Image* image = malloc(sizeof(Image));
	if(image==NULL)
	{
		printf("Allocation failure!");
	}
    uint64_t dataPosition = 0;
    int i = 0;
    int alignmentBytes = 0;
    int imgSize;
    int bpc;
    bmp_dib_header dibHeader;


    FILE* inputStream = fopen(imagePath, "r");
	if(inputStream==NULL)
	{
		printf("Invalid path!");
	}
    //JUMP TO bfOffBits Byte 10 - 13
    fseek(inputStream, 10, SEEK_CUR);
    
    dataPosition = readLittleEndian(inputStream, 4);
   /* for( i=0; i<3 ; i++ )
    {
    dataPosition |= (fgetc(inputStream)<<i*8);
    }*/

    fseek(inputStream, 4, SEEK_CUR);
    dibHeader.width = (uint32_t) readLittleEndian(inputStream, 4);
    dibHeader.height = (uint32_t)readLittleEndian(inputStream, 4);
    fseek(inputStream, 2, SEEK_CUR);
    dibHeader.bpp= (uint16_t)readLittleEndian(inputStream, 2);
    imgSize = dibHeader.width*dibHeader.height;
    image->size.height=dibHeader.height;
    image->size.width=dibHeader.width;
    bpc = dibHeader.bpp / 3;


    //ALLOC Image Data
    image->data = (Pixel*) malloc(imgSize*sizeof(Pixel));

    // JUMP TO BMPDATA
    fseek(inputStream, dataPosition, SEEK_SET);

    /* Get the count of alignment Bytes needed */
    while ((dibHeader.bpp/8*dibHeader.width+alignmentBytes)%4 != 0)
    {
        alignmentBytes++;
    }
    int index;
    while(i<imgSize-1)
    {
        index = imgSize - (i/dibHeader.width+1)*dibHeader.width + i%dibHeader.width;
        //index = i;
        image->data[index].b = (uint8_t)readLittleEndian(inputStream, bpc/8);
        image->data[index].g = (uint8_t)readLittleEndian(inputStream, bpc/8);
        image->data[index].r = (uint8_t)readLittleEndian(inputStream, bpc/8);
        
        // TODO Alignment
        if(i%dibHeader.width == dibHeader.width-1)
        {
            char c;
            for(int i=0; i<alignmentBytes && (c= fgetc(inputStream))!= EOF;i++);

        }
        i++;
    }


    return image;

}

/**
* readLittleEndian
* reads an unsigned number from a number of bytes(littleEndian)
*
* @param 	inputStream  	The Stream, the number should be read from
*           numberOfBytes   The Number of bytes that contains a number
* @return   uint64_t 		The Read number
*/

uint64_t readLittleEndian(FILE* inputStream, int numberOfBytes)
{
    uint64_t number =0;
    for (int i=0; i<numberOfBytes; i++)
    {
        number |= fgetc(inputStream) <<8*i;
    }
    return number;
}

/**
* hexDumpImageData
* dumps the image data array in stdout, in hexadecimal format
*
* @param 	image   image you want to dump
*/

void hexDumpImageData(Image* image)
{
    for (unsigned int i=0; i<image->size.height*image->size.width; i++)
    {
        printf("%02X%02X%02X ",image->data[i].r,image->data[i].g,image->data[i].b);
        if(i%image->size.width == image->size.width-1)
        {
            printf("\n");
        }
    }
}

